home *** CD-ROM | disk | FTP | other *** search
/ MACD 5 / MACD 5.bin / workbench / tools / czesc_2 / fifolib / fifo.c < prev    next >
C/C++ Source or Header  |  1992-04-24  |  10KB  |  505 lines

  1.  
  2. /*
  3.  *  FIFO.C
  4.  */
  5.  
  6. #include "defs.h"
  7.  
  8. void ReCalcReaderIdx(Fifo *);
  9. void SignalEof(Fifo *);
  10. void FixFiFlags(Fifo *);
  11. LibCall void RequestFifo(FHan *, Message *, long);
  12. void SetEOF(Fifo *);
  13. long AvailWBufSpace(Fifo *);
  14.  
  15. __stkargs long BitTestSet(char *, short);
  16.  
  17. /*
  18.  *  Open up a new fifo by name.
  19.  */
  20.  
  21. LibCall FHan *
  22. OpenFifo(name, bytes, flags)
  23. char *name;
  24. long bytes;
  25. long flags;
  26. {
  27.     Fifo *fifo;
  28.     FHan *fhan;
  29.  
  30.     {                /*  bytes a power of 2? */
  31.     unsigned long i = 8;
  32.     while (i) {
  33.         if (bytes == i)
  34.         break;
  35.         i = i << 1;
  36.     }
  37.     if (i == 0)
  38.         return(NULL);
  39.     }
  40.     if (fhan = AllocMem(sizeof(FHan), MEMF_PUBLIC | MEMF_CLEAR)) {
  41.     Forbid();
  42.     if (fifo = (Fifo *)FindName((MaxList *)&FifoList, name)) {
  43.         ;
  44.     } else {
  45.         if (fifo = AllocMem(sizeof(Fifo) - sizeof(fifo->fi_Buf) + bytes + strlen(name) + 1, MEMF_PUBLIC | MEMF_CLEAR)) {
  46.         AddTail((MaxList *)&FifoList, &fifo->fi_Node);
  47.         NewList((MaxList *)&fifo->fi_HanList);
  48.         NewList((MaxList *)&fifo->fi_WrNotify);
  49.         NewList((MaxList *)&fifo->fi_RdNotify);
  50.         fifo->fi_BufSize = bytes;
  51.         fifo->fi_BufMask = bytes - 1;
  52.         fifo->fi_Node.ln_Name = (char *)fifo + (sizeof(Fifo) - sizeof(fifo->fi_Buf)) + bytes;
  53.         strcpy(fifo->fi_Node.ln_Name, name);
  54.         InitSemaphore(&fifo->fi_SigSem);
  55.         }
  56.     }
  57.     if (fifo) {
  58.         AddTail((MaxList *)&fifo->fi_HanList, (MaxNode *)&fhan->fh_Node);
  59.         fhan->fh_Fifo = fifo;
  60.         fhan->fh_Flags = flags;
  61.  
  62.         fhan->fh_Msg.mn_ReplyPort = &fhan->fh_Port;
  63.  
  64.         fhan->fh_Port.mp_Node.ln_Type = NT_MSGPORT;
  65.         fhan->fh_Port.mp_Flags = PA_SIGNAL;
  66.         fhan->fh_Port.mp_SigBit = SIGB_SINGLE;
  67.  
  68.         NewList(&fhan->fh_Port.mp_MsgList);
  69.  
  70.         if (flags & FIFOF_READ) {
  71.         ++fifo->fi_RRefs;
  72.         fhan->fh_RIdx = fifo->fi_RIdx;
  73.         }
  74.         if (flags & FIFOF_WRITE) {
  75.         ++fifo->fi_WRefs;
  76.         }
  77.         /*
  78.         if (flags & FIFOF_CLOSEOF)
  79.         fifo->fi_Flags |= FIFOF_CLOSEOF;
  80.         */
  81.         if (flags & FIFOF_KEEPIFD)
  82.         fifo->fi_Flags |= FIFOF_KEEPIFD;
  83.         if (flags & FIFOF_RREQUIRED)
  84.         fifo->fi_Flags |= FIFOF_RREQUIRED;
  85.         ++fifo->fi_ORefs;
  86.         FixFiFlags(fifo);
  87.     } else {
  88.         FreeMem(fhan, sizeof(FHan));
  89.         fhan = NULL;
  90.     }
  91.     Permit();
  92.     }
  93.     return(fhan);
  94. }
  95.  
  96. /*
  97.  *  Close a previously openned fifo
  98.  */
  99.  
  100. LibCall void
  101. CloseFifo(fhan, flags)
  102. FHan *fhan;
  103. long flags;
  104. {
  105.     Fifo *fifo;
  106.  
  107.     Forbid();
  108.     if (fifo = fhan->fh_Fifo) {
  109.     fhan->fh_Fifo = NULL;    /*  try to catch duplicate closes   */
  110.     Remove((MaxNode *)&fhan->fh_Node);
  111.  
  112.     --fifo->fi_ORefs;
  113.  
  114.     if (fhan->fh_Flags & FIFOF_WRITE) {
  115.         if (flags & FIFOF_EOF)
  116.         fifo->fi_Flags |= FIFOF_CLOSEOF;
  117.         if (--fifo->fi_WRefs == 0) {
  118.         if (fifo->fi_Flags & FIFOF_CLOSEOF) {
  119.             SetEOF(fifo);
  120.         }
  121.         }
  122.     }
  123.     if (fhan->fh_Flags & FIFOF_READ) {
  124.         --fifo->fi_RRefs;
  125.  
  126.         if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED)) {
  127.         Message *msg;
  128.         while (msg = RemHead((MaxList *)&fifo->fi_WrNotify))
  129.             ReplyMsg(msg);
  130.         }
  131.     }
  132.  
  133.     if (fifo->fi_ORefs == 0) {
  134.         if ((fifo->fi_Flags & FIFOF_KEEPIFD) == 0 || fifo->fi_RIdx == fifo->fi_WIdx) {
  135.         Remove(&fifo->fi_Node);
  136.         FreeMem(fifo, sizeof(Fifo) - sizeof(fifo->fi_Buf) + fifo->fi_BufSize + strlen(fifo->fi_Node.ln_Name) + 1);
  137.         }
  138.     } else {
  139.         if (fhan->fh_Flags & FIFOF_WRITE)
  140.         SignalEof(fifo);
  141.         if (fhan->fh_Flags & FIFOF_READ)
  142.         ReCalcReaderIdx(fifo);
  143.         FixFiFlags(fifo);
  144.     }
  145.     FreeMem(fhan, sizeof(FHan));
  146.     }
  147.     Permit();
  148. }
  149.  
  150. /*
  151.  *  Read from a fifo.  Block if necessary (and not FIFOF_NBIO)
  152.  */
  153.  
  154. LibCall long
  155. ReadFifo(fhan, pbuf, skip)
  156. FHan *fhan;
  157. char **pbuf;
  158. long skip;
  159. {
  160.     long n;
  161.     Fifo *fifo = fhan->fh_Fifo;
  162.  
  163.     /*
  164.      *    attempt to clear <skip> bytes
  165.      */
  166.  
  167.     while (skip > 0) {
  168.     long widx = fifo->fi_WIdx;    /*  snapshot widx   */
  169.     long ridx = fhan->fh_RIdx;
  170.     long len;
  171.  
  172.     if (ridx <= widx) {
  173.         len = widx - ridx;
  174.     } else {
  175.         len = fifo->fi_BufSize - ridx;
  176.     }
  177.     if (len == 0)
  178.         break;
  179.     if (len > skip)
  180.         len = skip;
  181.     n += len;
  182.     skip -= len;
  183.  
  184.     Forbid();
  185.     fhan->fh_RIdx = (ridx + len) & fifo->fi_BufMask;
  186.     if (ridx == fifo->fi_RIdx)
  187.         ReCalcReaderIdx(fifo);      /*  update mast-idx/writer-waiters */
  188.     Permit();
  189.     }
  190.  
  191.     /*
  192.      *    return available data
  193.      */
  194.  
  195.     for (;;) {
  196.     long widx = fifo->fi_WIdx;    /*  snapshot widx   */
  197.     long ridx = fhan->fh_RIdx;
  198.  
  199.     if (ridx <= widx) {
  200.         n = widx - ridx;
  201.     } else {
  202.         n = fifo->fi_BufSize - ridx;
  203.     }
  204.     *pbuf = fifo->fi_Buf + ridx;
  205.     if (n == 0) {
  206.         /*
  207.          *    EOF on a per-handle basis since it gets cleared after EOF
  208.          *    is returned.
  209.          */
  210.  
  211.         if ((fhan->fh_Flags & FIFOF_EOF) || (fifo->fi_Flags & FIFOF_EOF)) {
  212.         /*fhan->fh_Flags &= ~FIFOF_EOF;*/
  213.         n = -1;
  214.         break;
  215.         }
  216.         if ((fhan->fh_Flags & FIFOF_NBIO) == 0) {
  217.         fhan->fh_Port.mp_SigTask = SysBase->ThisTask;
  218.         RequestFifo(fhan, &fhan->fh_Msg, FREQ_RPEND);
  219.         WaitPort(&fhan->fh_Port);
  220.         Remove((MaxNode *)&fhan->fh_Msg);
  221.         continue;
  222.         }
  223.     }
  224.     break;
  225.     }
  226.     return(n);
  227. }
  228.  
  229. /*
  230.  *  Write to a fifo
  231.  */
  232.  
  233. LibCall long
  234. WriteFifo(fhan, buf, bytes)
  235. FHan *fhan;
  236. char *buf;
  237. long bytes;
  238. {
  239.     long n = -1;
  240.     short wsigchk = 0;
  241.     short normal  = 0;
  242.     Fifo *fifo = fhan->fh_Fifo;
  243.  
  244.     if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED))
  245.     return(-1);
  246.  
  247.     if (bytes < 0 || bytes > (fifo->fi_BufSize >> 1))
  248.     return(-2);
  249.  
  250.     Forbid();
  251.  
  252.     /*
  253.      *    A normal FIFO uses fi_SigSem
  254.      *    A non-normal FIFO cannot afford to block and uses fi_Lock
  255.      */
  256.  
  257.     if (fifo->fi_Flags & FIFOF_WRNORM)
  258.     normal = 1;
  259.  
  260.     if (normal) {
  261.     ObtainSemaphore(&fifo->fi_SigSem);
  262.     } else if (BitTestSet(&fifo->fi_Lock, 0) != 0) {
  263.     Permit();
  264.     return(n);
  265.     }
  266.  
  267.     {
  268.     n = 0;
  269.  
  270.     for (;;) {
  271.         while (bytes) {
  272.         long ridx = fifo->fi_RIdx;    /*  snapshot ridx   */
  273.         long len;
  274.  
  275.         if (fifo->fi_WIdx < ridx) {
  276.             len = ridx - fifo->fi_WIdx;
  277.             if (len <= bytes)           /*  FIFO FULL       */
  278.             break;
  279.         } else {
  280.             len = fifo->fi_BufSize - fifo->fi_WIdx;
  281.             if (len + ridx <= bytes)    /*  FIFO FULL       */
  282.             break;
  283.         }
  284.         if (len > bytes)
  285.             len = bytes;
  286.         movmem(buf, fifo->fi_Buf + fifo->fi_WIdx, len);
  287.         buf += len;
  288.         n += len;
  289.         bytes -= len;
  290.         fifo->fi_WIdx = (fifo->fi_WIdx + len) & fifo->fi_BufMask;
  291.         if (fhan->fh_Flags & FIFOF_NORMAL)
  292.             wsigchk = 1;
  293.         }
  294.  
  295.         /*
  296.          *    if fifo is full and NBIO not set, then block and loop
  297.          */
  298.         if (bytes && !(fhan->fh_Flags & FIFOF_NBIO)) {
  299.         fhan->fh_Port.mp_SigTask = SysBase->ThisTask;
  300.         RequestFifo(fhan, &fhan->fh_Msg, FREQ_WAVAIL);
  301.         WaitPort(&fhan->fh_Port);
  302.         Remove((MaxNode *)&fhan->fh_Msg);
  303.         continue;
  304.         }
  305.         break;
  306.     }
  307.  
  308.     /*
  309.      *  check for any blocked readers, data is probably now available
  310.      *  so wake them up.
  311.      */
  312.  
  313.     if (wsigchk && fifo->fi_RdNotify.mlh_Head->mln_Succ) {
  314.         Message *msg;
  315.  
  316.         while (msg = RemHead((MaxList *)&fifo->fi_RdNotify))
  317.         ReplyMsg(msg);
  318.     }
  319.  
  320.     if (normal)
  321.         ReleaseSemaphore(&fifo->fi_SigSem);
  322.     else
  323.         fifo->fi_Lock = 0;
  324.     }
  325.     Permit();
  326.     return(n);
  327. }
  328.  
  329. LibCall long
  330. BufSizeFifo(fhan)
  331. FHan *fhan;
  332. {
  333.     return(fhan->fh_Fifo->fi_BufSize);
  334. }
  335.  
  336.  
  337. /*
  338.  *  Calculate distance between fh->fh_RIdx and fifo->fi_RIdx, shortest
  339.  *  wins.  If none found then nothing changes due to initial best set
  340.  *  to exactly the buffer size.
  341.  *
  342.  *  If the master index is modified and writers are waiting, signal them.
  343.  *
  344.  *  Called while Forbid() or otherwise reader-lockedout
  345.  */
  346.  
  347. void
  348. ReCalcReaderIdx(fifo)
  349. Fifo *fifo;
  350. {
  351.     FHan *fh;
  352.     long bestLen = fifo->fi_BufSize;
  353.     long ridx;
  354.  
  355.     for (fh = fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = fh->fh_Node.mln_Succ) {
  356.     if (fh->fh_Flags & FIFOF_READ) {
  357.         long len = (fh->fh_RIdx - fifo->fi_RIdx) & fifo->fi_BufMask;
  358.         if (len < bestLen)
  359.         bestLen = len;
  360.     }
  361.     }
  362.  
  363.     ridx = (fifo->fi_RIdx + bestLen) & fifo->fi_BufMask;
  364.  
  365.     /*
  366.      *    more buffer space available, wakeup writers?
  367.      */
  368.  
  369.     if (ridx != fifo->fi_RIdx) {
  370.     fifo->fi_RIdx = ridx;
  371.  
  372.     if (fifo->fi_WrNotify.mlh_Head->mln_Succ) {
  373.         Message *msg;
  374.  
  375.         if (AvailWBufSpace(fifo) >= (fifo->fi_BufSize >> 1)) {
  376.         while (msg = RemHead((MaxList *)&fifo->fi_WrNotify))
  377.             ReplyMsg(msg);
  378.         }
  379.     }
  380.     }
  381. }
  382.  
  383. /*
  384.  *  signal EOF to any blocked readers
  385.  */
  386.  
  387. void
  388. SignalEof(fifo)
  389. Fifo *fifo;
  390. {
  391.     FHan *fh;
  392.  
  393.     if (fifo->fi_Flags & FIFOF_EOF) {
  394.     Message *msg;
  395.  
  396.     Forbid();
  397.     while (msg = RemHead((MaxList *)&fifo->fi_RdNotify))
  398.         ReplyMsg(msg);
  399.     Permit();
  400.     }
  401. }
  402.  
  403. /*
  404.  *  SetEOF()
  405.  */
  406.  
  407. void
  408. SetEOF(fifo)
  409. Fifo *fifo;
  410. {
  411.     FHan *fh;
  412.  
  413.     for (fh = fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = fh->fh_Node.mln_Succ)
  414.     fh->fh_Flags |= FIFOF_EOF;
  415.     fifo->fi_Flags |= FIFOF_EOF;
  416. }
  417.  
  418. /*
  419.  *  FIXME, if state change occurs in master fifo, must unblock anybody
  420.  *  blocked due to previous information. XXX
  421.  */
  422.  
  423. void
  424. FixFiFlags(fifo)
  425. Fifo *fifo;
  426. {
  427.     FHan *fh;
  428.     long rflags = FIFOF_RDNORM | FIFOF_WRNORM;
  429.  
  430.     fifo->fi_Flags &= ~rflags;
  431.     for (fh = fifo->fi_HanList.mlh_Head; fh->fh_Node.mln_Succ; fh = fh->fh_Node.mln_Succ) {
  432.     if ((fh->fh_Flags & FIFOF_NORMAL) == 0) {
  433.         if (fh->fh_Flags & FIFOF_READ)
  434.         rflags &= ~FIFOF_RDNORM;
  435.         if (fh->fh_Flags & FIFOF_WRITE)
  436.         rflags &= ~FIFOF_WRNORM;
  437.     }
  438.     }
  439.     fifo->fi_Flags |= rflags;
  440. }
  441.  
  442. /*
  443.  *  request message on event.  Returns message immediately if event already
  444.  *  satisfied.
  445.  */
  446.  
  447. LibCall void
  448. RequestFifo(fhan, msg, req)
  449. FHan *fhan;
  450. Message *msg;
  451. long req;
  452. {
  453.     Fifo *fifo = fhan->fh_Fifo;
  454.  
  455.     Forbid();
  456.  
  457.     switch(req) {
  458.     case FREQ_RPEND:
  459.     if ((fhan->fh_Flags & FIFOF_EOF) || fhan->fh_RIdx != fifo->fi_WIdx) {
  460.         ReplyMsg(msg);
  461.     } else {
  462.         msg->mn_Node.ln_Type = NT_MESSAGE;
  463.         AddTail((MaxList *)&fifo->fi_RdNotify, &msg->mn_Node);
  464.     }
  465.     break;
  466.     case FREQ_WAVAIL:
  467.     /*
  468.      *  determine available buffer space, alert if more than 1/2 empty.
  469.      *
  470.      *  check for broken pipe
  471.      */
  472.  
  473.     if (fifo->fi_RRefs == 0 && (fifo->fi_Flags & FIFOF_RREQUIRED)) {
  474.         ReplyMsg(msg);
  475.     } else if (AvailWBufSpace(fifo) >= (fifo->fi_BufSize >> 1)) {
  476.         ReplyMsg(msg);
  477.     } else {
  478.         msg->mn_Node.ln_Type = NT_MESSAGE;
  479.         AddTail((MaxList *)&fifo->fi_WrNotify, &msg->mn_Node);
  480.     }
  481.     break;
  482.     case FREQ_ABORT:
  483.     if (msg->mn_Node.ln_Type == NT_MESSAGE) {   /*  if not returned */
  484.         Remove(&msg->mn_Node);                  /*  return it       */
  485.         ReplyMsg(msg);
  486.     }
  487.     break;
  488.     }
  489.     Permit();
  490. }
  491.  
  492. long
  493. AvailWBufSpace(fifo)
  494. Fifo *fifo;
  495. {
  496.     long ridx = fifo->fi_RIdx;
  497.     long len;
  498.  
  499.     if (fifo->fi_WIdx < ridx)
  500.     len = ridx - fifo->fi_WIdx;
  501.     else
  502.     len = fifo->fi_BufSize - fifo->fi_WIdx + ridx - 1;
  503.     return(len);
  504. }
  505.